﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;

namespace anks.Lib.Logging
{
    public class LoggingLevel
    {
        public LoggingLevel(Logging logging, string name)
        {
            this.logging = logging;
            this.name = name;
        }

        public void Log(string message)
        {
            logging.Log(this, message);
        }

        public Logging logging { get; set; }
        public string name { get; set; }
    }

    public abstract class CustomLoggingListener
    {
        public abstract void DoLog(Logging logging, string levelName, string message);
    }

    public class Logging
    {
        public const string levelName_Debug = "debug";
        public const string levelName_Info = "info";
        public const string levelName_Warning = "warning";
        public const string levelName_Error = "error";
        public const string levelName_All = "all";

        private class ListenerRegistration
        {
            public string _levelName;
            public CustomLoggingListener _listener;
        }

        private void AddDefaultLevels()
        {
            AddLevel(levelName_Debug);
            AddLevel(levelName_Info);
            AddLevel(levelName_Warning);
            AddLevel(levelName_Error);
            AddLevel(levelName_All);
        }

        public Logging()
        {
            _levels = new Dictionary<string, LoggingLevel>();
            _listeners = new List<ListenerRegistration>();
            AddDefaultLevels();
        }

        public LoggingLevel AddLevel(string name)
        {
            LoggingLevel level = new LoggingLevel(this, name);
            levels.Add(name, level);
            return level;
        }

        public void DeleteLevel(LoggingLevel level)
        {
            DeleteLevel(level.name);
        }

        public void DeleteLevel(string name)
        {
            DeleteListener(name);
            levels.Remove(name);
        }

        public void AddListener(LoggingLevel level, CustomLoggingListener listener)
        {
            AddListener(level.name, listener);
        }

        public void AddListener(string levelName, CustomLoggingListener listener)
        {
            _listeners.Add(new ListenerRegistration { _levelName = levelName, _listener = listener });
        }

        public void DeleteListener(CustomLoggingListener listener)
        {
            _listeners.RemoveAll(p => p._listener == listener);
        }

        public void DeleteListener(LoggingLevel level, CustomLoggingListener listener)
        {
            DeleteListener(level.name, listener);
        }

        public void DeleteListener(string levelName, CustomLoggingListener listener)
        {
            _listeners.RemoveAll(p => (p._listener == listener) && (p._levelName == levelName));
        }

        public void DeleteListener(LoggingLevel level)
        {
            DeleteListener(level.name);
        }

        public void DeleteListener(string levelName)
        {
            _listeners.RemoveAll(p => p._levelName == levelName);
        }

        public void Log(LoggingLevel level, string message)
        {
            Log(level.name, message);
        }

        public void Log(string levelName, string message)
        {
            foreach (CustomLoggingListener listener in listeners[levelName])
                try
                {
                    listener.DoLog(this, levelName, message);
                }
                catch (Exception) { };
            
            if (levelName != all.name)
                all.Log(message);
        }

        private Dictionary<string, LoggingLevel> _levels;
        public Dictionary<string, LoggingLevel> levels { get { return _levels; } }

        private List<ListenerRegistration> _listeners;
        public Lookup<string, CustomLoggingListener> listeners { 
            get {
                return (Lookup<string, CustomLoggingListener>)_listeners.ToLookup(p => p._levelName, p => p._listener); 
            } 
        }

        public LoggingLevel debug {get {return levels[levelName_Debug]; } }
        public LoggingLevel info {get {return levels[levelName_Info]; } }
        public LoggingLevel warning {get {return levels[levelName_Warning]; } }
        public LoggingLevel error {get {return levels[levelName_Error]; } }
        public LoggingLevel all { get { return levels[levelName_All]; } }
    }
}

